﻿namespace MicroCloud.Utils
{
    /// <summary>
    /// 类型 <see cref="Type"/> 辅助扩展方法类
    /// </summary>
    public static class TypeExtensions
    {
        #region "类型<see cref="Type"/>辅助扩展方法"
        #region "判断当前类型是否可由指定类型派生"
        /// <summary>
        /// 判断当前类型是否可由指定类型派生
        /// </summary>
        /// <typeparam name="TBaseType">基础类型</typeparam>
        /// <param name="type">要处理的类型</param>
        /// <param name="canAbstract">是否包含抽象类</param>
        /// <returns></returns>
        public static bool IsDeriveClassFrom<TBaseType>(this Type type, bool canAbstract = false)
        {
            return IsDeriveClassFrom(type, typeof(TBaseType), canAbstract);
        }
        #endregion
        #region "判断当前类型是否可由指定类型派生"
        /// <summary>
        /// 判断当前类型是否可由指定类型派生
        /// </summary>
        /// <param name="type">要判断的类型</param>
        /// <param name="baseType">基础类型</param>
        /// <param name="canAbstract">是否抽象类</param>
        /// <returns></returns>
        public static bool IsDeriveClassFrom(this Type type, Type baseType, bool canAbstract = false)
        {
            Check.NotNull(type, nameof(type));
            Check.NotNull(baseType, nameof(baseType));

            return type.IsClass && (canAbstract || !type.IsAbstract) && type.IsBaseOn(baseType);
        }
        #endregion
        #region "判断当前类型是否存在派生类型"
        /// <summary>
        /// 判断当前类型是否存在派生类型
        /// </summary>
        /// <param name="type">要判断的类型</param>
        /// <param name="hasAbstract">包含抽象类</param>
        /// <returns></returns>
        public static bool HasDeriveClass(this Type type, bool hasAbstract = false)
        {
            Check.NotNull(type, nameof(type));

            var flag = AssemblyManager.HasDerivedClass(type, hasAbstract);
            return flag;
        }
        #endregion
        #region "判断类型是否为Nullable类型"
        /// <summary>
        /// 判断类型是否为Nullable类型
        /// </summary>
        /// <param name="type">要处理的类型</param>
        /// <returns>是返回True，不是返回False。</returns>
        public static bool IsNullableType(this Type type)
        {
            return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }
        #endregion
        #region "由类型的Nullable类型返回实际类型"
        /// <summary>
        /// 由类型的Nullable类型返回实际类型
        /// </summary>
        /// <param name="type">要处理的类型</param>
        /// <returns>实际类型</returns>
        public static Type GetNonNullableType(this Type type)
        {
            return IsNullableType(type) ? type.GetGenericArguments()[0] : type;
        }
        #endregion
        #region "通过类型转换器获取Nullable类型的基础类型"
        /// <summary>
        /// 通过类型转换器获取Nullable类型的基础类型
        /// </summary>
        /// <param name="type">要处理的类型</param>
        /// <returns>基础类型</returns>
        public static Type GetUnNullableType(this Type type)
        {
            if (IsNullableType(type))
            {
                NullableConverter nullableConverter = new(type);
                return nullableConverter.UnderlyingType;
            }
            return type;
        }
        #endregion
        #region "获取类型的 Description 特性描述信息"
        /// <summary>
        /// 获取类型的 Description 特性描述信息
        /// </summary>
        /// <param name="type">类型对象</param>
        /// <param name="inherit">是否搜索类型的继承链以查找描述特性</param>
        /// <returns>返回 Description 特性描述信息，如不存在则返回类型的全名。</returns>
        public static string GetDescription(this Type type, bool inherit = true)
        {
            //DescriptionAttribute desc = type.GetAttribute<DescriptionAttribute>(inherit);
            //return desc == null ? type.FullName : desc.Description;
            DescriptionAttribute desc = type.GetAttribute<DescriptionAttribute>(inherit);
            if (desc != null)
            {
                return desc.Description;
            }
            DisplayNameAttribute displayName = type.GetAttribute<DisplayNameAttribute>(inherit);
            if (displayName != null)
            {
                return displayName.DisplayName;
            }
            DisplayAttribute display = type.GetAttribute<DisplayAttribute>(inherit);
            if (display != null)
            {
                return display.Name;
            }
            return type.FullName;
        }
        #endregion
        #region "判断类型是否为集合类型"
        /// <summary>
        /// 判断类型是否为集合类型
        /// </summary>
        /// <param name="type">要处理的类型</param>
        /// <returns>是返回True，不是返回False。</returns>
        public static bool IsEnumerable(this Type type)
        {
            if (type == typeof(string))
            {
                return false;
            }
            return typeof(IEnumerable).IsAssignableFrom(type);
        }
        #endregion
        #region "判断当前泛型类型是否可由指定类型的实例填充"
        /// <summary>
        /// 判断当前泛型类型是否可由指定类型的实例填充
        /// </summary>
        /// <param name="genericType">泛型类型</param>
        /// <param name="type">指定类型</param>
        /// <returns></returns>
        public static bool IsGenericAssignableFrom(this Type genericType, Type type)
        {
            genericType.CheckNotNull(nameof(genericType));
            type.CheckNotNull(nameof(type));
            if (!genericType.IsGenericType)
            {
                throw new ArgumentException(I18N.T("该功能只支持泛型类型的调用，非泛型类型可使用 IsAssignableFrom 方法。"));
            }

            List<Type> allOthers = new() { type };
            if (genericType.IsInterface)
            {
                allOthers.AddRange(type.GetInterfaces());
            }

            foreach (var other in allOthers)
            {
                Type cur = other;
                while (cur != null)
                {
                    if (cur.IsGenericType)
                    {
                        cur = cur.GetGenericTypeDefinition();
                    }
                    if (cur.IsSubclassOf(genericType) || cur == genericType)
                    {
                        return true;
                    }
                    cur = cur.BaseType;
                }
            }
            return false;
        }
        #endregion
        #region "返回当前类型是否是指定基类的派生类"
        /// <summary>
        /// 返回当前类型是否是指定基类的派生类
        /// </summary>
        /// <param name="type">当前类型</param>
        /// <param name="baseType">要判断的基类型</param>
        /// <returns></returns>
        public static bool IsBaseOn(this Type type, Type baseType)
        {
            if (type == null || baseType == null || type == baseType)
            {
                return false;
            }
            if (baseType.IsGenericTypeDefinition)
            {
                return baseType.IsGenericAssignableFrom(type);
            }
            return baseType.IsAssignableFrom(type);
        }
        #endregion
        #region "返回当前类型是否是指定基类的派生类"
        /// <summary>
        /// 返回当前类型是否是指定基类的派生类
        /// </summary>
        /// <typeparam name="TBaseType">要判断的基类型</typeparam>
        /// <param name="type">当前类型</param>
        /// <returns></returns>
        public static bool IsBaseOn<TBaseType>(this Type type)
        {
            Type baseType = typeof(TBaseType);
            return type.IsBaseOn(baseType);
        }
        #endregion
        #region "获取类型的全名(附带所在类库)"
        /// <summary>
        /// 获取类型的全名(附带所在类库)
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetFullNameWithModule(this Type type)
        {
            return $"{type.FullName},{type.Module.Name.Replace(".dll", "").Replace(".exe", "")}";
        }
        #endregion
        #region "获取类型的显示短名称"
        /// <summary>
        /// 获取类型的显示短名称
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string ShortDisplayName(this Type type)
        {
            return type.DisplayName(false);
        }
        #endregion
        #region "获取类型的显示名称"
        /// <summary>
        /// 获取类型的显示名称
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fullName"></param>
        /// <returns></returns>
        public static string DisplayName([NotNull] this Type type, bool fullName = true)
        {
            StringBuilder sb = new();
            ProcessType(sb, type, fullName);
            return sb.ToString();
        }
        #endregion

        #endregion

        #region "私有字段"
        #region "内置类型名称(只读)"
        /// <summary>
        /// 内置类型名称(只读)
        /// </summary>

        private static readonly Dictionary<Type, string> _builtInTypeNames = new()
        {
            { typeof(bool), "bool" },
            { typeof(byte), "byte" },
            { typeof(char), "char" },
            { typeof(decimal), "decimal" },
            { typeof(double), "double" },
            { typeof(float), "float" },
            { typeof(int), "int" },
            { typeof(long), "long" },
            { typeof(object), "object" },
            { typeof(sbyte), "sbyte" },
            { typeof(short), "short" },
            { typeof(string), "string" },
            { typeof(uint), "uint" },
            { typeof(ulong), "ulong" },
            { typeof(ushort), "ushort" },
            { typeof(void), "void" }
        };
        #endregion

        #endregion

        #region "类型<see cref="Type"/>辅助扩展方法-私有方法"
        #region "进程类型"
        /// <summary>
        /// 进程类型
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="type"></param>
        /// <param name="fullName"></param>
        private static void ProcessType(StringBuilder builder, Type type, bool fullName)
        {
            if (type.IsGenericType)
            {
                var genericArguments = type.GetGenericArguments();
                ProcessGenericType(builder, type, genericArguments, genericArguments.Length, fullName);
            }
            else if (type.IsArray)
            {
                ProcessArrayType(builder, type, fullName);
            }
            else if (_builtInTypeNames.TryGetValue(type, out var builtInName))
            {
                builder.Append(builtInName);
            }
            else if (!type.IsGenericParameter)
            {
                builder.Append(fullName ? type.FullName : type.Name);
            }
        }
        #endregion
        #region "进程数组类型"
        /// <summary>
        /// 进程数组类型
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="type"></param>
        /// <param name="fullName"></param>
        private static void ProcessArrayType(StringBuilder builder, Type type, bool fullName)
        {
            var innerType = type;
            while (innerType.IsArray)
            {
                innerType = innerType.GetElementType();
            }

            ProcessType(builder, innerType, fullName);

            while (type.IsArray)
            {
                builder.Append('[');
                builder.Append(',', type.GetArrayRank() - 1);
                builder.Append(']');
                type = type.GetElementType();
            }
        }
        #endregion
        #region "进程泛型类型"
        /// <summary>
        /// 进程泛型类型
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="type"></param>
        /// <param name="genericArguments"></param>
        /// <param name="length"></param>
        /// <param name="fullName"></param>
        private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, bool fullName)
        {
            var offset = type.IsNested ? type.DeclaringType.GetGenericArguments().Length : 0;

            if (fullName)
            {
                if (type.IsNested)
                {
                    ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, fullName);
                    builder.Append('+');
                }
                else
                {
                    builder.Append(type.Namespace);
                    builder.Append('.');
                }
            }

            var genericPartIndex = type.Name.IndexOf('`');
            if (genericPartIndex <= 0)
            {
                builder.Append(type.Name);
                return;
            }

            builder.Append(type.Name, 0, genericPartIndex);
            builder.Append('<');

            for (var i = offset; i < length; i++)
            {
                ProcessType(builder, genericArguments[i], fullName);
                if (i + 1 == length)
                {
                    continue;
                }

                builder.Append(',');
                if (!genericArguments[i + 1].IsGenericParameter)
                {
                    builder.Append(' ');
                }
            }

            builder.Append('>');
        }
        #endregion

        #endregion

    }

}
